package sql;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import arbre.utils.ArbreBinaire;
import arbre.utils.Chemin;
import arbre.utils.ContentNode;
import arbre.utils.Noeud;
import arbre.utils.Point;
import arbre.utils.Polyedre;
import arbre.utils.Segment;
import arbre.utils.Zone;

public class Recuperator {

	private static Connection connection;

	public Recuperator(){
		connection = Connector.connecter();
	}

	int zone = 0;

	public static String map_xml = "map.xml";

	public static String fichierBatiments = "recuperer_batiments.sql";
	public List<Polyedre> recupererBatiments(){
		List<Polyedre> polyedres = new ArrayList<Polyedre>();

		String sql = Lector.lire(fichierBatiments);
		try{
			Statement statement = connection.createStatement();
			ResultSet resultSet = statement.executeQuery(sql);

			while (resultSet.next()) {

				Polyedre polyedre = new Polyedre();

				String id = resultSet.getString("osm_id");
				polyedre.id = id;

				String name = resultSet.getString("name");
				//System.out.println("name: " + name);

				if(name == null || name.equals("NULL"))
					name = "zone"+zone;

				polyedre.nom = name;

				String geometry = resultSet.getString("geo");
				//System.out.println("geometry: " + geometry);

				//String geometrySon = resultSet.getString("geoSon");
				//System.out.println("geometrySon: " + geometrySon);

				String path = resultSet.getString("path");
				//System.out.println("path: " + path);

				int z_order = resultSet.getInt("z_order");
				//System.out.println("z_order: " + z_order);

				polyedre.z_order = z_order;

				String building = resultSet.getString("building");
				//System.out.println("building: " + building);

				String landuse = resultSet.getString("landuse");
				//System.out.println("landuse: " + landuse);

				if(path.equals("{0}"))
				{

					if(building != null && !building.equals("NULL")){
						if(building.equals("yes"))
							polyedre.type="building";
						else
							polyedre.type=building;
					}

					if(landuse != null && !landuse.equals("NULL"))
						polyedre.type=landuse;
				}
				else
				{
					polyedre.type="trou";
					polyedre.id = ""+zone;
				}

				String geometry2 = geometry.replace("POLYGON","").replace("(", "").replace(")", "");
				//System.out.println("geometry2: " + geometry2);

				String[] points = geometry2.split(",");

				Point[] pts = new Point[points.length];

				int i=0;
				for(String point : points){
					String[] point_xy = point.split(" ");
					Float x = Float.parseFloat(point_xy[0])*1000;
					Float y = Float.parseFloat(point_xy[1])*1000;

					Point pt = new Point(x,y);
					pts[i++]=pt;

					//System.out.println("POINT "+pt.toString());
				}

				polyedre.points = pts;

				//System.out.println(polyedre.toString());

				zone++;
				polyedres.add(polyedre);
			}

		}catch(Exception e){
			e.printStackTrace();
		}

		return polyedres;


	}

	public Chemin contain(List<Chemin> chemins , String id)
	{
		for(Chemin c : chemins)
		{
			if(c.getId().equals(id))
				return c;
		}
		return null;
	}
	
	public static String fichierAllRoadsLines = "recuperer_routes.sql";
	private List<Chemin> recupererRoutes(){
		String sql = Lector.lire(fichierAllRoadsLines);

		//System.out.println(sql);

		List<Chemin> chemins = new ArrayList<Chemin>();

		try{
			Statement statement = connection.createStatement();
			ResultSet resultSet = statement.executeQuery(sql);

			while (resultSet.next()) {
				String id= resultSet.getString("id");

				String type = resultSet.getString("highway");
				String lignes = resultSet.getString("lignes");

				Chemin chemin = contain(chemins, id);
				if(chemin == null)
				{
					chemin = new Chemin();
					chemins.add(chemin);
				}

				if(type ==  null || type.equals("NULL")){
					type="autre";
				}
				else
				{
					if(type.equals("secondary") || type.equals("primary") 
							|| type.equals("residential") || type.equals("tertiary")
							|| type.equals("secondary_link") 
							|| type.equals("primary_link")
							|| type.equals("tertiary_link")
							//apparement les unclassified de la base sont des routes !
							||type.equals("unclassified"))
						type="route";
					else if(type.equals("track") || type.equals("service")
							||type.equals("pedestrian")
							||type.equals("footway"))
						type="chemin";
					else
						type="autre";
				}

				chemin.setType(type);
				chemin.setId(id);

				List<Segment> segments = new ArrayList<Segment>();

				lignes = lignes.replace("(", "").replace(")","").replace("LINESTRING","");
				String[] points = lignes.split(",");
				//conversion en points
			ArrayList<Point> allpoint=new ArrayList<Point>();
				for(int i=0;i < points.length;i++)
				{
					String[] points1 = points[i].split(" ");
					float x = Float.parseFloat(points1[0])*1000;
					float y = Float.parseFloat(points1[1])*1000;
					allpoint.add(new Point(x, y));
				}
				Point pred = null;
				int i=0;
				for(Point p : allpoint){
					if(pred != null)
					{

						Segment segment = new Segment(pred,p);
						if(chemin.getSegments().size()!=0)
						{
							//si le chemin existe deja on cherche le max id pour
							//ajouter dans ce chemin un segment d'id different
							String maxid = chemin.getId();
							for(Segment s : chemin.getSegments())
							{
								if(s.getId().compareTo(maxid) > 0)
									maxid=s.getId();
							}
							/*							
							//<debug>
							if(maxid.equals("477549410_0"))
							{
								System.out.println("size "+chemin.getSegments().size());
								String ids = "";
								for(Segment s : chemin.getSegments())
									ids+=" "+s.getId();
								System.out.println(");
							}
							//</debug>*/
							int j = 0;
							try{
								 j = Integer.parseInt(maxid.split("_")[1]);
							}
							catch(Exception e)
							{
								System.out.println("error :" + maxid);
							}
							segment.setId(id+"_"+(1000*i+(j+1)));
						}
						else
						{
							//chemin non trouve => ajout classique
							segment.setId(id+"_"+i);
						}
						chemin.getSegments().add(segment);
						if(i>0){
							Segment sPred = segments.get(i-1);
							sPred.addIntersection(segment.getId());
							segment.addIntersection(sPred.getId());
						}
						
						i++;
						segment.setType(type);
						segments.add(segment);
						
					}
					pred = p;
				}


				//chemin.getSegments().addAll(segments);
				//chemins.add(chemin);
			}

		}catch(Exception e){
			e.printStackTrace();
		}

		//System.out.println(segments.toString());

		return chemins;


	}

	/**
	 * retourne les Segment les plus proches entre s1d, s1f et s2d , s2f
	 * @param s1d segment de debut du premier chemin
	 * @param p1 point de s1d en contact avec le 2e chemin
	 * @param s1f segment de fin du premier chemin
	 * @param p2 point de s1f en contact avec le 2e chemin
	 * @param s2d segment de debut du 2e chemin
	 * @param p3 point de s2d en contact avec le 2e chemin
	 * @param s2f segment de fin du 2e chemin
	 * @param p4 point de s2f en contact avec le 2e chemin
	 * @return le tabelau contenant les deux segments
	 */
	private Segment[] getMinDistance(Segment s1d, Point p1, Segment s1f,Point p2,
			Segment s2d, Point p3,Segment s2f,Point p4 )
	{
		Segment[] res = new Segment[2];
		double[] distres = new double[4];
		distres[0] = Segment.distance(p1, p3);
		distres[1] = Segment.distance(p1, p4);
		distres[2] = Segment.distance(p2, p3);
		distres[3] = Segment.distance(p2, p4);
		
		double min_value =distres[0];
		int min_ind = 0;
		for(int i=0; i<distres.length;i++)
		{
			if(distres[i]<min_value)
			{
				min_ind = i;
				min_value = distres[i];
			}
		}
		if(min_ind == 0)
		{
			res[0]=s1d;
			res[1]= s2d;
		}
		if(min_ind == 1)
		{
			res[0]=s1d;
			res[1]=s2f;
		}

		if(min_ind == 2)
		{
			res[0]=s1f;
			res[1]= s2d;
		}
		if(min_ind == 3)
		{
			res[0]=s1f;
			res[1]= s2f;
		}
		return res;
	}
	
	public void getMinDistance(Chemin c1, Chemin c2){
		
		if(c1.getId() != c2.getId())
		{
			for(Segment s1 : c1.getSegments())
			{
				Point p11 = s1.getDebut();
				Point p12 = s1.getFin();
				for(Segment s2 : c2.getSegments())
				{
					Point p21 = s2.getDebut();
					Point p22 = s2.getFin();
					if(s1.getId().equals("4775494_01") || s2.getId().equals("1107863449_3"))
					{
						System.out.println("trouver");
					}
					if((p11.equalsWithEps(p21) || p12.equalsWithEps(p21) ||
							p11.equalsWithEps(p22) || 
							p12.equalsWithEps(p22)) && ( 
							!s1.intersections.contains(s2) || !s2.intersections.contains(s1)))
					{
						s1.addIntersection(s2.getId());
						s2.addIntersection(s1.getId());
					}
				}
			}
		}
		else
		{
			System.err.println("erreur d'appel intersection getMin");
		}
		
	}
	
	
	public static String fichierIntersectionsRoutes = "intersections_routes.sql";
	public int recupererIntersectionsRoutes(Map<String,Chemin> chemins){

		String sql = Lector.lire(fichierIntersectionsRoutes);

		int nb_intersect = 0;

		try{
			Statement statement = connection.createStatement();
			ResultSet resultSet = statement.executeQuery(sql);

			while (resultSet.next()) {

				String id1 = resultSet.getString("id1");
				String id2 = resultSet.getString("id2");

				//System.out.println(id1+" | "+id2);

				try{
					if(chemins.containsKey(id1) && chemins.containsKey(id2)){
						Chemin chemin1 = chemins.get(id1);
						chemin1.addIntersections(id2);

						Chemin chemin2 = chemins.get(id2);
						chemin2.addIntersections(id1);
						getMinDistance(chemin1, chemin2);
						/*
						Segment s1_debut = chemin1.getSegments().get(0);
						Segment s1_fin = chemin1.getSegments().get(chemin1.getSegments().size()-1);
						
						Segment s2_debut = chemin2.getSegments().get(0);
						Segment s2_fin = chemin2.getSegments().get(chemin2.getSegments().size()-1);
						
						Segment[] intersects = getMinDistance(s1_debut, s1_debut.getDebut(),
								s1_fin, s1_fin.getDebut(), s2_debut, s2_debut.getDebut(),
								s2_fin, s2_fin.getDebut());
						//<debug>
						/*if(intersects[0].getId().contains("1107863449_3") || 
								intersects[1].getId().contains("1107863449_3"))
						{
							System.out.println("trouve");
						}
						//</debug>
						
						intersects[0].addIntersection(intersects[1].getId());
						intersects[1].addIntersection(intersects[0].getId());
						*/
//						for(Segment s1 : chemin1.getSegments()){
//							for(Segment s2 : chemin2.getSegments()){
//								if(s1.intersection(s2) != null){
//									s1.addIntersection(s2.getId());
//									s2.addIntersection(s1.getId());
//								}
//
//							}
//						}
						nb_intersect++;
					}
				}
				catch(Exception e )
				{
					System.err.println("erreur recuperation intersection_routes : "+id1+"|"+id2);
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}

		return nb_intersect;
	}

	public static String fichierIntersectionsBatimentsRoutes = "intersections_batiments_routes.sql";
	public int recupererIntersectionsBatimentsRoutes(Map<String,Polyedre> batiments,Map<String,Chemin> routes){

		String sql = Lector.lire(fichierIntersectionsBatimentsRoutes);

		int nb_intersect = 0;

		try{
			Statement statement = connection.createStatement();
			ResultSet resultSet = statement.executeQuery(sql);

			while (resultSet.next()) {

				String id1 = resultSet.getString("id1");
				String id2_source = resultSet.getString("id2_source");
				String id2_target = resultSet.getString("id2_target");
				String intersections = resultSet.getString("intersections");

				Point pointIntersection = null;

				intersections = intersections.replace("(", "").replace(")","").replace("LINESTRING","");
				//remplace toute autre lettre pour les diferrent type de collection
				intersections = intersections.replaceAll("[A-Z]+","");
				String[] points = intersections.split(",");
				//calcul du point d'intersection
				//TODO attention la bdd retourne une linestring on prend a chaque fois le dernier point
				for(int i=0;i<1;i++){

					String[] points1 = points[i].split(" ");
					float x1 = Float.parseFloat(points1[0]);
					float y1 = Float.parseFloat(points1[1]);

					String[] points2 = points[i+1].split(" ");
					float x2 = Float.parseFloat(points2[0]);
					float y2 = Float.parseFloat(points2[1]);

					Point p1 = new Point(x1,y1);
					Point p2 = new Point(x2,y2);

					pointIntersection = p1;
				}



				System.out.print(".");

				if( batiments.containsKey(id1) && routes.containsKey(id2_target) ){
					Polyedre batiment = batiments.get(id1);

					if( !batiment.osmIdRouteParcourus.contains(id2_source)  )
					{
						Chemin chemin = routes.get(id2_target);
						//pour tous les segments
						for(Segment segment : chemin.getSegments()){

							//if(segment.contains(pointIntersection)){
							if(! segment.polygones.contains(id1) 
									&& 
									! batiment.getIntersections().contains(segment.getId()))
							{
								segment.polygones.add(id1);
								batiment.addIntersections(segment.getId());
								nb_intersect++;
							}	
							else
							{
								/*System.err.println("le batiment "
										+batiment.getId()+" contient déja le segment "+segment.getId());*/
							}
							//}
						}
						batiment.osmIdRouteParcourus.add(id2_source);
					}
				}
				else
				{
					//System.err.println("couple zone/ route inconnue !");
				}
			}
		}catch(Exception e){
			e.printStackTrace();
		}

		System.out.println("");

		return nb_intersect;
	}


	public static Map<String,Chemin> getMapChemins(List<Chemin> chemins){
		Map<String,Chemin> idsChemins = new HashMap<String,Chemin>();
		for(Chemin chemin : chemins){
			idsChemins.put(chemin.getId(), chemin);
			//System.out.println(chemin.getId()+" ajouté");
		}
		return idsChemins;
	}

	public static Map<String,Polyedre> getMapBatiments(List<Polyedre> batiments){
		Map<String,Polyedre> idsBatiments = new HashMap<String,Polyedre>();
		for(Polyedre batiment : batiments){
			idsBatiments.put(batiment.getId(), batiment);
			//System.out.println(batiment.getId()+" ajouté");
		}
		return idsBatiments;
	}


	/**
	 * @param zones liste des polygons de la carte
	 * @param routes liste des routes segmente de la carte
	 * @param links relation id routes => liste des ids de zone coupant ou touchant la routes, arametre de sortie
	 */
	public static String fichierRecuperationZones="route_alex.sql";
	public static Map<String,ArrayList<String> > getIntersectionRoadPolygon( Map <String,Polyedre> zones, 
			Map<String, Chemin> routes)
			{
		String sql = Lector.lire(fichierRecuperationZones);

		Map<String,ArrayList<String> > links = new HashMap<String, ArrayList<String> >();

		try{
			Statement statement = connection.createStatement();
			ResultSet resultSet = statement.executeQuery(sql);

			while (resultSet.next()) 
			{
				String route = resultSet.getString("route");
				//String id2_source = resultSet.getString("route_source");
				String zone = resultSet.getString("zone");

				if(zones.get(zone) != null)
				{
					if(routes.get(route) != null)
					{
						if(links.containsKey(route))
						{
							ArrayList<String> intersects = links.get(route);
							if(intersects.contains(zone))
								intersects.add(zone);
							links.put(route, intersects);
						}
						else
						{
							ArrayList<String> intersects = new ArrayList<String>();
							intersects.add(zone);
							links.put(route, intersects);
						}
					}
					else
					{
						System.err.println("la route "+route+" n'existe pas dans la map ");
					}
				}
				else
				{
					System.err.println("la zone "+zone+" n'existe pas dans la map ");
				}

			}

		}
		catch(Exception e)
		{
			e.printStackTrace();
		}

		return links;

			}


	static boolean charger = true;

	private static float epsilon=0.001f;
	public static void main(String[] args){

		List<Polyedre> polyedres = new ArrayList<Polyedre>();
		List<Chemin> chemins = new ArrayList<Chemin>();
		Map<String,ArrayList<String> > intersects = new HashMap<String, ArrayList<String>>();

		if(charger){
			Recuperator r = new Recuperator();
			System.out.println("Recuperation des batiments");
			polyedres = r.recupererBatiments();

			System.out.println("Recuperation des routes");
			chemins = r.recupererRoutes();
			Map<String,Chemin> cheminsMap = getMapChemins(chemins);
			Map<String,Polyedre> batimentsMap = getMapBatiments(polyedres);

			System.out.println("Recuperation des intersections entre chemins");
			int nb_intersections = r.recupererIntersectionsRoutes(cheminsMap);
			System.out.println(nb_intersections+" intersections de chemins trouvees");
			System.out.println("Recuperation des intersections des batiments");
			int nb_intersectionsBatiments = r.recupererIntersectionsBatimentsRoutes(batimentsMap,cheminsMap);
			System.out.println(nb_intersectionsBatiments+" intersections de batiments trouvees");

			System.out.println("calcul des intersection zone/route");
			intersects.putAll(getIntersectionRoadPolygon(batimentsMap,cheminsMap));
			int nb_intersect = 0;
			for(Entry<String,ArrayList<String> > e : intersects.entrySet())
				nb_intersect+=e.getValue().size();
			System.out.println(intersects.entrySet().size()+" routes intersecten avec "+
					nb_intersect+" zones");
			System.out.println("serialisation");

			Serializor.sauvegarder(polyedres,chemins,intersects);
		}else{
			Serializor.charger(polyedres, chemins,intersects);
			System.out.println(polyedres.size()+" batiments et "+chemins.size()+" routes ecrits");
		}

		for(Chemin chemin : chemins){
			for(int i=1;i<chemin.getSegments().size();++i){
				Segment segment = chemin.getSegments().get(i);
				Segment sPred = chemin.getSegments().get(i-1);
				sPred.addIntersection(segment.getId());
				segment.addIntersection(sPred.getId());
			}
		}

		Writor.ecrire(map_xml, Writor.toMap(polyedres,chemins));
		System.out.println(polyedres.size()+" batiments et "+chemins.size()+" routes ecrits");

		List<Segment> lesSegmentsPolyedres = Polygon2Segments4ArbreBinaire(polyedres);
		System.out.println("de  : "+polyedres.size()+" poly -> "+lesSegmentsPolyedres.size());
		ArbreBinaire arbrePolygon = new ArbreBinaire(lesSegmentsPolyedres);
		System.out.println("arbrepoly : "+arbrePolygon.taille());
		System.out.println("arbrePolygon cree.");
	
//		Serializor.sauvegarderArbre("arbrePolyedres.data", arbrePolygon);
//		ArbreBinaire arbreBinaire = new ArbreBinaire();
//		Noeud r = new Noeud(null, null, null, new ContentNode("zone", "test",-10000f,-10000f), 0);
//		arbreBinaire.setRacine(r);
//		Serializor.sauvegarderArbre("arbreTest.data", arbreBinaire);
//
		Serializor.saveTreeJSON("arbrePolygon.json", arbrePolygon);
		
		ArbreBinaire ar2 = new ArbreBinaire();
		Serializor.loadTreeJSON("arbrePolygon.json", ar2);
		System.out.println("reload tree : "+ar2.taille());
		System.out.println("Ca passe");
		
		List<Polyedre> polygon_chemins = cheminsToPolyedres(chemins);
		System.out.println("de  : "+chemins.size()+" chemins -> "+polygon_chemins.size());
		List<Segment> segments_chemins = CheminPolygon2Segments4ArbreBinaire(polygon_chemins);
		System.out.println("de  : "+polygon_chemins.size()+" polychemins -> "+segments_chemins.size());
		ArbreBinaire arbreChemins = new ArbreBinaire(segments_chemins);
		System.out.println("chemins : "+arbreChemins.taille());
		System.out.println("arbreChemins cree.");
		Serializor.saveTreeJSON("arbreChemin.json", arbreChemins);
		System.out.println("ça passe saveJSOn chemin");
		ArbreBinaire ar3 = new ArbreBinaire();
		Serializor.loadTreeJSON("arbreChemin.json", ar3);
		System.out.println(ar3.taille());

	}

	private static List<Polyedre> cheminsToPolyedres(List<Chemin> chemins) {
		ArrayList<Polyedre> res= new ArrayList<Polyedre>();
		//pour chaque segments du chemin, nous allons créer des polygons a partir des segments
		for(Chemin c: chemins){
			for(Segment s : c.getSegments()){
				Point[] lesPoints = new Point[4];
				lesPoints[0] = s.getDebut();
				lesPoints[1] = s.getFin();
				lesPoints[2] = new Point(s.getFin().getX()+epsilon, s.getFin().getY()+epsilon);
				lesPoints[3] = new Point(s.getDebut().getX()+epsilon, s.getDebut().getY()+epsilon);
				Polyedre lePolyedre = new Polyedre(lesPoints);
				lePolyedre.setId(s.getId());
				res.add(lePolyedre);
			}
		}

		return res;
	}


	public static List<Segment> Polygon2Segments4ArbreBinaire(List<Polyedre> polyedres){
		ArrayList<Segment> res = new ArrayList<Segment>();
		//les polyedres
		for(Polyedre p : polyedres){
			Point [] lespoints =  p.points;
			for(int i= 0 ; i< lespoints.length-1;i++){
				if(!lespoints[i].equals(lespoints[i+1])){
					Segment s;
					s= new Segment(lespoints[i], lespoints[i+1]);
					s.setId(p.id+"_"+i);
					s.setType(p.type);
					if(!res.contains(s)){
						affecterZonePolyedre(s, p);
						res.add(s);
					}
				}
			}
		}
		return res;
	}
	public static List<Segment> CheminPolygon2Segments4ArbreBinaire(List<Polyedre> polyedres){
		ArrayList<Segment> res = new ArrayList<Segment>();
		//les polyedres
		for(Polyedre p : polyedres){
			Point [] lespoints =  p.points;
			for(int i= 0 ; i< lespoints.length-1;i++){
				if(!lespoints[i].equals(lespoints[i+1])){
					Segment s;
					s= new Segment(lespoints[i], lespoints[i+1]);
					s.setId(p.id);
					s.setType(p.type);
					if(!res.contains(s)){
						affecterZonePolyedre(s, p);
						res.add(s);
					}
				}
			}
		}
		return res;
	}




	private static void affecterZonePolyedre(Segment s,
			Polyedre polyedres) { 
		/*
		 * calculer le sens du segment
		 */
		if(s.getDebut().compareTo(s.getFin()) <0){
			//debut est à gauche ou en dessous de fin
			s.setHaut(new Zone(polyedres.getId()));
			s.setBas(ZONE_DEFAULT);
		}
		else if (s.getDebut().compareTo(s.getFin()) >0){
			//debut tets en haut ou a droite de fin
			s.setBas(new Zone(polyedres.getId()));
			s.setHaut(ZONE_DEFAULT);
		}


	}

	public static final Zone ZONE_DEFAULT = new Zone("DEFAULT");

}

